package com.db.williamchart.extensions;

import com.db.williamchart.data.DataPoint;
import com.db.williamchart.data.Label;
import ohos.agp.render.Path;
import ohos.agp.utils.Point;
import ohos.utils.Pair;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class ListEx {

    public static Pair<Float, Float> limits(List<DataPoint> list){
        if (list.isEmpty()){
            return new Pair<>(0f, 1f);
        }
        List<Float> values = new ArrayList<>();
        for (DataPoint dataPoint : list){
            values.add(dataPoint.getValue());
        }

        return floatLimits(values);
    }
    public static List<Label> toLabels(List<DataPoint> list){
        List<Label> values = new ArrayList<>();
        for (DataPoint dataPoint : list){
            values.add(new Label(dataPoint.getLabel(),0f,0f));
        }
        return values;
    }

//    public static Float maxValueBy(Iterable<Label> selector){
//        Iterator<Label> iterator = selector.iterator();
//
//        if (!iterator.hasNext()) return null;
//
//        Label maxElem = iterator.next();
//        Float maxValue = maxElem. ;
//        if (!iterator.hasNext()) return maxValue;
//
//        do {
//            Label e = iterator.next();
//            Float v = m(e);
//            if (maxValue < v){
//                maxValue = v;
//            }
//        }while (iterator.hasNext());
//    }

    public static <T,R> List<R> map(List<T> list,R r){
        List<R> list1 = new ArrayList<>();
        for (T t: list){
            list1.add(r);
        }
        return list1;
    }

    public static Pair<Float, Float> floatLimits(List<Float> list){
        Float min = min(list);
        if (min == null){
            min = 0f;
        }
        Float max = max(list);
        if (max == null){
            max = 1f;
        }
        if (Math.abs(max - min) < 1e-6f){
            max += 1F;
        }
        return new Pair<>(min,max);
    }


    public static Float min(List<Float> list){
        Iterator<Float> iterator = list.iterator();
        if (!iterator.hasNext()) {
            return null;
        }
        float min = iterator.next();
        while (iterator.hasNext()) {
            float e = iterator.next();
            min = Math.min(min, e);
        }
        return min;
    }
    public static Float max(List<Float> list){
        Iterator<Float> iterator = list.iterator();
        if (!iterator.hasNext()) {
            return null;
        }
        float max = iterator.next();
        while (iterator.hasNext()) {
            float e = iterator.next();
            max = Math.max(max, e);
        }
        return max;
    }

    public static <K,V> List<Pair<K,V>> toList(Map<K,V> map){
        if (map.size() == 0){
            return new ArrayList<>();
        }
        Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator();
        if (!iterator.hasNext()){
            return new ArrayList<>();
        }
        Map.Entry<K, V> first = iterator.next();
        if (!iterator.hasNext()){
            List<Pair<K,V>> list = new ArrayList<>();
            list.add(new Pair<>(first.getKey(),first.getValue()));
            return list;
        }
        List<Pair<K,V>> result = new ArrayList<>(map.size());
        result.add(new Pair<>(first.getKey(),first.getValue()));
        do {
            result.add(new Pair<>(iterator.next().getKey(),iterator.next().getValue()));
        }while (iterator.hasNext());
        return result;
    }

    public static Path toLinePath(List<DataPoint> list){
        Path res = new Path();
        if (!list.isEmpty()){
            int size = list.size();
            res.moveTo(new Point(list.get(0).getScreenPositionX(),list.get(0).getScreenPositionY()));
            for (int i = 1; i <= size ; i++) {
                res.lineTo(new Point(list.get(i).getScreenPositionX(),list.get(i).getScreenPositionY()));
            }
        }
        return res;
    }
    public static Path toSmoothLinePath(List<DataPoint> list,Float smoothFactor){
        Float thisPointX;
        Float thisPointY;
        Float nextPointX;
        Float nextPointY;
        Float startDiffX;
        Float startDiffY;
        Float endDiffX;
        Float endDiffY;
        float firstControlX;
        float firstControlY;
        float secondControlX;
        float secondControlY;

        Path res = new Path();
        if (!list.isEmpty()){
            int size = list.size();
            res.moveTo(new Point(list.get(0).getScreenPositionX(),list.get(0).getScreenPositionY()));

            for (int i = 0; i < size - 1; i++) {
                thisPointX = list.get(i).getScreenPositionX();
                thisPointY = list.get(i).getScreenPositionY();

                nextPointX = list.get(i + 1).getScreenPositionX();
                nextPointY = list.get(i + 1).getScreenPositionY();

                startDiffX = nextPointX - list.get(si(list.size(), i - 1)).getScreenPositionX();
                startDiffY = nextPointY - list.get(si(list.size(), i - 1)).getScreenPositionY();

                endDiffX = list.get(si(list.size(), i + 2)).getScreenPositionX() - thisPointX;
                endDiffY = list.get(si(list.size(), i + 2)).getScreenPositionY() - thisPointY;

                firstControlX = thisPointX + smoothFactor * startDiffX;
                firstControlY = thisPointY + smoothFactor * startDiffY;

                secondControlX = nextPointX - smoothFactor * endDiffX;
                secondControlY = nextPointY - smoothFactor * endDiffY;

                res.cubicTo(
                        new Point(firstControlX,
                        firstControlY),
                        new Point(secondControlX,
                        secondControlY),
                        new Point(nextPointX,
                        nextPointY)
                );
            }
        }
        return res;
    }
    private static Integer si(int setSize,int i){
        if ( i > setSize - 1){
            return setSize - 1;
        }else return Math.max(i, 0);
    }
}
